//------------------------------------------------------------------------------
// <copyright company="Telligent Systems">
//     Copyright (c) Telligent Systems Corporation.  All rights reserved.
// </copyright> 
//------------------------------------------------------------------------------

using System;
using System.Collections;
using System.Collections.Specialized;
using System.Data;
using CommunityServer.Components;
using CommunityServer.Configuration;
using System.IO;
using System.Xml;

namespace CommunityServer.Discussions.Components
{
    /// <summary>
    /// Summary description for ForumsDataProvider.
    /// </summary>
    public abstract class ForumDataProvider
    {
        public ForumDataProvider()
        {
          
        }

        public static readonly string ForumDataProviderName = "ForumDataProvider";

        //public abstract SearchResultSet GetSearchResults(SearchBarrelCriteria barrelCriteria);
		public abstract PostSet SearchReindexPosts (int setsize, int settingsID);
		public abstract ThreadSet SearchIndexPosts (int setsize, ForumThreadQuery query);

        #region Instance

        private static ForumDataProvider _defaultInstance = null;

        static ForumDataProvider()
        {
            CreateDefaultCommonProvider();
        }

        /// <summary>
        /// Returns an instance of the user-specified data provider class.
        /// </summary>
        /// <returns>An instance of the user-specified data provider class.  This class must inherit the
        /// CommonDataProvider interface.</returns>
        public static ForumDataProvider Instance() 
        {
            return _defaultInstance;
        }

        public static ForumDataProvider Instance (Provider dataProvider) 
        {
            ForumDataProvider fdp = CSCache.Get(dataProvider.Name) as ForumDataProvider;
            if(fdp == null)
            {
                fdp = DataProviders.Invoke(dataProvider) as ForumDataProvider;
                CSCache.Max(dataProvider.Name,fdp);
            }
            return fdp;
        }

        /// <summary>
        /// Creates the Default CommonDataProvider
        /// </summary>
        private static void CreateDefaultCommonProvider()
        {
            // Get the names of the providers
            //
            CSConfiguration config = CSConfiguration.GetConfig();

            // Read the configuration specific information
            // for this provider
            //
            Provider sqlForumsProvider = (Provider) config.Providers[ForumDataProviderName];

            // Read the connection string for this provider
            //
            
            _defaultInstance = DataProviders.CreateInstance(sqlForumsProvider) as ForumDataProvider;
        }
        
        #endregion

        #region Base Methods

        #region Posts
        public abstract PostSet GetPosts(int postID, int pageIndex, int pageSize, int sortBy, int sortOrder, int userID, bool returnRecordCount, bool includeCategories);
		public abstract PostSet GetPosts_RSSThreadless(int sectionID, int pageSize);
		public abstract void UpdatePostCategories(int forumID, int postID, string[] categoryList);
		public abstract ArrayList GetPostCategories(int postID);
        public abstract ForumPost GetPost(int postID, int userID, bool trackViews, bool markRead, bool includeCategories);
        public abstract ForumPost AddPost(ForumPost postToAdd, int userID);
        public abstract void UpdatePost(ForumPost post, int editedBy);

        public abstract PostSet GetTopNNewPosts(string username, int postCount);
        public abstract PostSet GetTopNPopularPosts(string username, int postCount, int days);
        
		public abstract int GetTotalPostCount();
		public abstract int GetPageIndex(int postID, int pageSize, int sortBy, int sortOrder);
        public abstract void MarkPostAsRead(int postID, string username);
        #endregion

        #region Threads
		public abstract Thread GetThread(int threadID, int userID);
        public abstract ThreadSet GetThreads(ForumThreadQuery query);
        public abstract int GetNextThreadID(int postID);
        public abstract int GetPrevThreadID(int postID);
        public abstract void UpdateThreadStatus (int threadID, ThreadStatus status);
        public abstract void DeleteThread (int sectionID, int threadID, bool resetStatistics);
        #endregion

        #region Forums

        public abstract Hashtable GetForums();

        public abstract int GetForumIDByPostID(int postID);
        public abstract void MarkAllForumsRead(int userID, int forumGroupID, int forumID, bool markThreadsRead);

        #endregion

        #region Private Messages
        public abstract void CreatePrivateMessage(ArrayList users, int threadID);
        public abstract void DeletePrivateMessage(int userID, ArrayList deleteList);
		public abstract HybridDictionary GetPrivateMessageRecipients(int threadID);

        public static PrivateMessage PopulatePrivateMessageFromIDataReader (IDataReader reader) 
        {
            PrivateMessage thread = new PrivateMessage();

            return (PrivateMessage) PopulateThreadFromIDataReader(thread, reader);
        }
        #endregion

        #region Permissions
        public abstract ArrayList GetForumPermissions(int sectionID);
        #endregion

        #region RSS
        public abstract void RssPingback(Hashtable pingbackList);
        #endregion

        #region Moderation

        public abstract PostSet GetPostsToModerate(int forumID, int pageIndex, int pageSize, int sortBy, int sortOrder, int userID, bool returnRecordCount); //
        public abstract ArrayList GetForumsToModerate (int userID, ApplicationType applicationType); //
        public abstract bool ApprovePost(int postID, int userIDApprovedBy);
        public abstract bool CheckIfUserIsModerator(int userID, int forumID);
        public abstract void ThreadSplit (int postID, int moveToForumID, int splitByUserID);
        public abstract void ThreadJoin (int parentThreadID, int childThreadID, int joinedByUserID);
        public abstract void ModeratorDeletePost(int postID, int deletedBy, string reason, bool deleteChildPosts);

        //      bool DeleteModeratedPost(int postID, string approvedBy);
        public abstract bool CanEditPost(String Username, int PostID);
        public abstract MovedPostStatus MovePost(int postID, int moveToForumID, int movedBy);
		public abstract void UpdatePostParent(int postID, int parentID, int movedBy);
        public abstract bool UserHasPostsAwaitingModeration(String Username);

        //public abstract ArrayList GetForumsModeratedByUser(String Username);
        //public abstract ArrayList GetForumsNotModeratedByUser(String Username);
        //public abstract void AddModeratedForumForUser(ModeratedForum forum);
        // public abstract void RemoveModeratedForumForUser(ModeratedForum forum);
        //public abstract ArrayList GetModeratorsInterestedInPost(int PostID);
        //public abstract ArrayList GetForumModerators(int ForumID);
		public abstract ModerationQueueStatus GetQueueStatus(int sectionID, string username);
        public abstract ArrayList GetForumModeratorRoles (int forumID);
        public abstract void TogglePostSettings(ModeratePostSetting setting, ForumPost post, int moderatorID);
        public abstract void ToggleUserSettings(ModerateUserSetting setting, User user, int moderatorID);

        #endregion

		#region Threads Read
		public abstract HybridDictionary GetThreadsRead (int sectionID, int userID);
		#endregion

		#endregion

        #region Populate

        // *********************************************************************
        //
        //  PopulateThreadFromIDataReader
        //
        /// <summary>
        /// This private method accepts a datareader and attempts to create and
        /// populate a thread class instance which is returned to the caller. For
        /// all practical purposes, a thread is simply a lightweigh version of a 
        /// post - no details, such as the body, are provided though and a thread is
        /// always considered the first post in a thread.
        /// </summary>
        //
        // ********************************************************************/
        public static Thread PopulateThreadFromIDataReader(IDataReader reader) 
        {
            Thread thread = new Thread();

            return PopulateThreadFromIDataReader(thread, reader);
        }

        public static Thread PopulateThreadFromIDataReader(Thread thread, IDataReader reader) 
        {

            thread.SectionID =              (int) reader["SectionID"];
            thread.PostID =					(int) reader["PostID"];
            thread.Replies =                (int) reader["TotalReplies"];
            thread.AuthorID =               (int) reader["UserID"];
            thread.Views =                  (int) reader["TotalViews"];
            thread.MostRecentPostID =       (int) reader["MostRecentPostID"];
            thread.ThreadID =               (int) reader["ThreadID"];
            thread.Subject =                (string) reader["Subject"];
            thread.Body =                   (string) reader["Body"];
            thread.Username =               (string) reader["PostAuthor"];
            thread.MostRecentPostAuthor =   (string) reader["MostRecentPostAuthor"];
            thread.MostRecentPostAuthorID = (int) reader["MostRecentPostAuthorID"];
            thread.PostDate =               (DateTime) reader["PostDate"];
            thread.ThreadDate =             (DateTime) reader["ThreadDate"];
            thread.StickyDate =             (DateTime) reader["StickyDate"];
            thread.IsLocked =               (bool) reader["IsLocked"];
            thread.IsSticky =               (bool) reader["IsSticky"];
            thread.HasRead =                Convert.ToBoolean(reader["HasRead"]);
            thread.TotalRatings =           (int) reader["TotalRatings"];
            thread.RatingSum =              (int) reader["RatingSum"];
            thread.EmoticonID =             (int) reader["ThreadEmoticonID"];
            thread.Status =                 (ThreadStatus) ((int) reader["ThreadStatus"]);
            thread.FormattedBody =          reader["FormattedBody"] as string;
            
            thread.PostConfiguration =      (PostConfiguration) reader["PostConfiguration"];

			SerializerData data = CommonDataProvider.PopulateSerializerDataIDataRecord(reader, SerializationType.Post);
			thread.SetSerializerData(data);
            
            return thread;
        }

        public static ForumPost PopulatePostSetFromIDataReader(IDataReader dr) 
        {
            ForumPost post = PopulatePostFromIDataReader(dr);
            post.Section = PopulateForumFromIDataReader(dr);
            post.User = CommonDataProvider.PopulateUserFromIDataReader(dr,CommonDataProvider.PopulateMembershipUserFromIDataReader(dr,null),false,false);

            return post;
        }

        /// <summary>
        /// Builds and returns an instance of the Post class based on the current row of an
        /// aptly populated IDataReader object.
        /// </summary>
        /// <param name="dr">The IDataReader object that contains, at minimum, the following
        /// columns: PostID, ParentID, Body, ForumID, PostDate, PostLevel, SortOrder, Subject,
        /// ThreadDate, ThreadID, Replies, Username, and Approved.</param>
        /// <returns>An instance of the Post class that represents the current row of the passed 
        /// in SqlDataReader, dr.</returns>
        public static ForumPost PopulatePostFromIDataReader(IDataReader dr) 
        {

            ForumPost post = new ForumPost();
            CommonDataProvider.PopulatePostFromIDataReader(dr,post);

            try 
            {
                 post.ThreadStarterUsername = Convert.ToString( dr["ThreadStarterAuthor"] ).Trim();
                  post.ThreadStartDate = Convert.ToDateTime( dr["ThreadStartDate"] );
            } 
            catch {}

            return post;

        }

        #region Forums 

        public static  Forum PopulateForumFromIDataReader(IDataReader dr) 
        {
            Forum s = new Forum();
            CommonDataProvider.PopulateSectionFromIDataRecord(dr, s);
            return s;
        }

//        public static  ModeratedForum PopulateModeratedForumFromIDataReader(IDataReader dr) 
//        {
//            ModeratedForum forum = new ModeratedForum();
//            forum.SectionID = Convert.ToInt32(dr["SectionID"]);
//            forum.GroupID = Convert.ToInt32(dr["GroupID"]);
//            forum.DateCreated = Convert.ToDateTime(dr["DateCreated"]);
//            forum.Description = Convert.ToString(dr["Description"]);
//            forum.Name = Convert.ToString(dr["Name"]);
//            forum.IsModerated = Convert.ToBoolean(dr["Moderated"]);
//            forum.DefaultThreadDateFilter = (ThreadDateFilterMode) Convert.ToInt32(dr["DaysToView"]);
//            forum.IsActive = Convert.ToBoolean(dr["Active"]);
//            forum.SortOrder = Convert.ToInt32(dr["SortOrder"]);
//            //forum.IsPrivate = Convert.ToBoolean(dr["IsPrivate"]);
//
//            return forum;
//        }

        #endregion


		public static ForumPost PopulatePostContentFromDataRow(DataRow dr, DataRelation relationShip) 
		{
			//Is this all of the data we need?
			ForumPost post = new ForumPost();

			post.SectionID =                (int) dr["SectionID"];
			post.PostID =					(int) dr["PostID"];
			post.Replies =                (int) dr["Replies"];
			post.Views =                  (int) dr["TotalViews"];
			//post.AggViews =				  (int) dr["AggViews"];
			post.ThreadID =               (int) dr["ThreadID"];
			post.Subject =                (string) dr["Subject"];
			post.Body =                   (string) dr["Body"];
			post.Username =               dr["PostAuthor"].ToString().Trim().Length > 0 ? (string) dr["PostAuthor"] : (string) dr["ThreadPostAuthor"];
			post.UserID =				  (int) dr["UserID"]; //pulls the acutal post user instead the UserID from the Thread Table
			post.PostDate =               (DateTime) dr["PostDate"];
			post.ThreadDate =             (DateTime) dr["ThreadDate"];
			post.IsLocked =               (bool) dr["IsLocked"];
			post.FormattedBody =           dr["FormattedBody"] as string;

			post.IsApproved =             (bool) dr["IsApproved"];


			post.PostLevel =        (int) dr["PostLevel"];
			post.ParentID = (int) dr["ParentID"];

			post.PostConfiguration =              (PostConfiguration) (int)dr["PostConfiguration"];
			post.ForumPostType =			(ForumPostType) dr["ApplicationPostType"];

			//posts from cs versions below 2.0 usertime might be null
			try{post.UserTime =               (DateTime) dr["UserTime"];}
			catch{post.UserTime =              post.PostDate;}


			post.Name =                    dr["PostName"] as string;

			post.RatingSum = (int)dr["RatingSum"];
			post.TotalRatings = (int)dr["TotalRatings"];

			post.Points = (int)dr["PostPoints"];
			post.PostMedia = (PostMediaType)dr["PostMedia"];

  			post.AttachmentFilename = dr["AttachmentFileName"] as string;
			if(post.AttachmentFilename != null)
				post.Attachment = CommonDataProvider.PopulatePostAttachmentMetaDataFromIDataRow(dr);
                
  
			SerializerData data = new SerializerData();
			data.Keys = dr["PostPropertyNames"] as string;
			data.Values = dr["PostPropertyValues"] as string;
			post.SetSerializerData(data);



			DataRow[] categories = dr.GetChildRows(relationShip);
			if(categories != null && categories.Length > 0)
			{
				post.Categories = new string[categories.Length];
				for(int i = 0; i<categories.Length; i++)
				{
					post.Categories[i] = categories[i]["Name"] as string;
				}
			}

			return post;
		}

		public static ForumPost PopulatePostEntryFromIDataReader(IDataReader reader)
		{
			return PopulatePostEntryFromIDataReader(reader,false);
		}

		public static ForumPost PopulatePostEntryFromIDataReader(IDataReader reader, bool isTopLevel) 
		{
			ForumPost post = new ForumPost();
			post.SectionID =                (int) reader["SectionID"];
			post.PostID =					(int) reader["PostID"];
			post.Replies =                (int) reader["Replies"];
			post.Views =                  (int) reader["TotalViews"];
			//post.AggViews =				  (int) reader["AggViews"];
			post.ThreadID =               (int) reader["ThreadID"];
			post.Subject =                (string) reader["Subject"];
			post.Body =                   (string) reader["Body"];
			post.Username =               reader["PostAuthor"].ToString().Trim().Length > 0 ? (string) reader["PostAuthor"] : (string) reader["ThreadPostAuthor"];
			post.UserID =				  (int) reader["UserID"]; //pulls the acutal post user instead the UserID from the Thread Table
			post.PostDate =               (DateTime) reader["PostDate"];
            
			post.ThreadDate =             (DateTime) reader["ThreadDate"];
			post.IsLocked =               (bool) reader["IsLocked"];
			post.IsApproved =             (bool) reader["IsApproved"];
			post.FormattedBody =           reader["FormattedBody"] as string;

			post.PostLevel =        (int) reader["PostLevel"];
			post.ParentID =         (int) reader["ParentID"];

			post.PostConfiguration =              (PostConfiguration) (int)reader["PostConfiguration"];
			post.ForumPostType =			(ForumPostType) reader["ApplicationPostType"];

			//posts from cs versions below 2.0 usertime might be null
			try{post.UserTime =               (DateTime) reader["UserTime"];}
			catch{post.UserTime =              post.PostDate;}

			post.Name =                    reader["PostName"] as string;

			post.RatingSum = (int)reader["RatingSum"];
			post.TotalRatings = (int)reader["TotalRatings"];
			post.Points = (int)reader["PostPoints"];
			post.PostMedia = (PostMediaType)reader["PostMedia"];

			if(isTopLevel)
			{
				post.AttachmentFilename = reader["AttachmentFileName"] as string;
				if(post.AttachmentFilename != null)
					post.Attachment = CommonDataProvider.PopulatePostAttachmentMetaDataFromIDataRecord(reader);
                
			}

			SerializerData data = CommonDataProvider.PopulateSerializerDataIDataRecord(reader, SerializationType.Post);
			post.SetSerializerData(data);

			return post;
		}



		#endregion


        public static void PopulateIndexPostFromIDataReader(IDataReader dr, ForumPost post) 
        {

            // Populate Post
            //
            post.PostID             = Convert.ToInt32(dr["PostID"]);
            post.ParentID           = Convert.ToInt32(dr["ParentID"]);
            post.FormattedBody      = Convert.ToString(dr["FormattedBody"]).Trim();
            post.Body               = Convert.ToString(dr["Body"]).Trim();
            post.SectionID          = Convert.ToInt32(dr["SectionID"]);
            post.PostDate           = Convert.ToDateTime(dr["PostDate"]);
            post.PostLevel          = Convert.ToInt32(dr["PostLevel"]);
            post.SortOrder          = Convert.ToInt32(dr["SortOrder"]);
            post.Subject            = Convert.ToString(dr["Subject"]).Trim();
            post.ThreadDate         = Convert.ToDateTime(dr["ThreadDate"]);
            post.ThreadID           = Convert.ToInt32(dr["ThreadID"]);
            post.Replies            = Convert.ToInt32(dr["Replies"]);
			post.Username			= dr["PostAuthor"].ToString().Trim().Length > 0 ? (string) dr["PostAuthor"] : (string) dr["ThreadPostAuthor"];
			post.IsApproved         = Convert.ToBoolean(dr["IsApproved"]);
            post.IsLocked           = Convert.ToBoolean(dr["IsLocked"]);
            post.Views              = Convert.ToInt32(dr["TotalViews"]);
			post.HasRead            = Convert.ToBoolean(dr["HasRead"]);
            post.UserHostAddress    = (string) dr["IPAddress"];
            post.PostType           = (PostContentType) dr["PostType"];
            post.EmoticonID			= (int) dr["EmoticonID"];
            post.PostConfiguration  = (PostConfiguration) dr["PostConfiguration"];
            post.UserID             = (int)dr["UserID"];
			post.Points				= Convert.ToInt32(dr["PostPoints"]);
			post.TotalRatings		= Convert.ToInt32(dr["PostTotalRatings"]);
			post.RatingSum			= Convert.ToInt32(dr["PostRatingSum"]);
			post.PostMedia			= (PostMediaType) dr["PostMedia"];
	    }

		public static object ConvertCategoriesToXML(string[] Categories)
		{
			if(Categories == null || Categories.Length == 0)
				return DBNull.Value;

			StringWriter sw = new StringWriter();
			XmlTextWriter writer = new XmlTextWriter(sw);
			writer.WriteStartElement("Categories");
			foreach (string category in Categories)
			{
				writer.WriteElementString("Category", category);
			}
			writer.Close();
			return sw.ToString();
		}

    }
}
